/*
   Author: Minh B. Do (Arizona State University)
 */
package edu.asu.sapa.ground.update;

import edu.asu.sapa.Planner;
import edu.asu.sapa.ground.Fluent;
import edu.asu.sapa.ground.FluentDB;
import edu.asu.sapa.ground.MathForm;
import edu.asu.sapa.ground.Operator;
import edu.asu.sapa.ground.Problem;
import edu.asu.sapa.ground.Proposition;
import edu.asu.sapa.ground.State;
import edu.asu.sapa.heuristic.PG;

/**
 * GMySet: Class to store the grounded "Set" structure, which is an action
 * effect related to changing the value of a continuous function. The "Set"
 * contains of 3 parts, the function in the left hand side, the assigment
 * indication (increase/decrease etc.) and the GMathForm structure as the right
 * hand side
 * 
 * @author Minh B. Do
 * @author J. Daniel Benton
 */
public class Set extends Update {
	public int op; // "=" : 0; "-=" : 1; "+=" : 2; "*=" : 3; "/=" : 4;
	public int id; // Ground func ID of left side
	public MathForm rightSide;

	private boolean isConstant;
	private boolean value;

	// public Set() {
	// id = 0;
	// op = 0;
	// rightSide = null;
	// }

	public Set(int op, int id, MathForm right) {
		this.id = id;
		this.op = op;
		rightSide = right;
	}

	public Set bind(float start, float duration, float end) {
		MathForm r = rightSide.bind(start, duration, end);
		if (r != rightSide)
			return new Set(op, id, r);
		return this;
	}

	public void relaxedUpdate(PG pg, State s, Context c, float time) {
	}
	public void relaxedStart(PG pg, State s, Context c, float time) {
	}
	public void relaxedFinish(PG pg, State s, Context c, float time) {
	}
	
	public boolean updateBefore(State s, Context c, float time) {
		FluentDB fluentDB = s.fluentDB;
		float value = rightSide.value(s,c);
		if (op == 0) {
			fluentDB.set(id,value);
			return true;
		}

		float v = fluentDB.get(id);
		if (v != v)
			return false;
		switch (op) {
		case 1:
			v += value;
			break;
		case 2:
			v -= value;
			break;
		case 3:
			v *= value;
			break;
		case 4:
			v /= value;
			break;
		default:
			v = Float.NaN;
		}

		fluentDB.set(id, v);
		return true;
	}
	
	public boolean startBefore(State s, Context c, float time) {
		return updateBefore(s,c, time);
	}
	public boolean startAfter(State s, Context c, float time) {
		return updateBefore(s,c, time);
	}

	public boolean finishBefore(State s, Context c, float time) {
		return true;
	}
	public boolean finishAfter(State s, Context c, float time) {
		return true;
	}

	public boolean updateAfter(State s, Context c, float time) {
		return false;
	}
	
	public boolean applicableBefore(State state, Context c) {
		if (op != 0) {
			float v = state.fluentDB.get(id);
			if (v != v)
				return false;
		}
		return true;
	}
	public boolean applicableAfter(State state, Context c) {
		return applicableBefore(state,c);
	}

	
	public boolean analyzeStatic(float dur) {
		isConstant = false;
		rightSide.analyzeStatic(dur);
		Fluent f = Problem.fluents.get(id);
		if (f.isConstant) {
			isConstant = true;
			id = 0;
			value = (rightSide.value() == f.value);
			assert value == true && op == 0;
			return value == true;
		}
		return true;
	}

	public void setLeftSide(int id) {
		this.id = id;
	}

	public int getLeftSide() {
		return id;
	}

	public int getOp() {
		return op;
	}

	public void setRightSide(MathForm m) {
		rightSide = m;
	}

	public MathForm getRightSide() {
		return rightSide;
	}

	public String toString() {
		String s = "(" + op + " ";

		s += id + " " + rightSide + ")";

		return s;
	}

	public int compareTo(Set o) {
		// assert this != null && o != null;
		if (id < o.id)
			return -1;
		if (id > o.id)
			return 1;
		// groups sets
		// (assignments | linear | geometric)
		if (op < o.op)
			return -1;
		if (op > o.op)
			return 1;
		return 0;
	}
	
	public int compareTo(Update u) {
		return compareTo((Set)u);
	}
	
	public void removeDependencies(Operator o) {
	}
	public void setDependencies(Operator o) {
	}

	public boolean isFail() {
		return op < 0 || op > 4;
	}

	public boolean isNoOp() {
		return op == 0 && id == 0 && rightSide == MathForm.NaN;
	}
	
	public void removeDependencies(int id) {
	}

}
